package com.hongyun.tms.infra.excel.listener;

import com.alibaba.excel.context.AnalysisContext;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.hongyun.tms.common.constants.DefaultCreateByConstant;
import com.hongyun.tms.common.dto.mwork.MWorkItemImportDto;
import com.hongyun.tms.common.enums.BizCode;
import com.hongyun.tms.common.utils.MapperingUtils;
import com.hongyun.tms.entity.MWorkItem;
import com.hongyun.tms.exception.BusinessRuntimeException;
import com.hongyun.tms.service.IMWorkItemService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 作业项信息导入
 *
 * @author liPeng
 * @date 2024-09-10
 */
@Slf4j
@Component
public class WorkItemListener extends DefaultExcelListener<MWorkItemImportDto> {

    @Autowired
    private IMWorkItemService itemService;

    public WorkItemListener(IMWorkItemService itemService) {
        this.itemService = itemService;
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        log.info("成功读取excel中【" + getRows().size() + "】条作业项目数据");

        List<MWorkItemImportDto> rows = getRows();

        // 1.Excel内部校验
        validateExcelData(rows);

        // 2.数据库校验
        validateDatabaseData(rows);

        // 3.保存数据
        saveWorkItems(rows);
    }

    /**
     * Excel 内部校验：检查是否有重复的作业项目和作业类型
     */
    private void validateExcelData(List<MWorkItemImportDto> rows) {
        Map<String, Integer> uniqueCheckMap = new HashMap<>();
        for (int i = 0; i < rows.size(); i++) {
            MWorkItemImportDto dto = rows.get(i);
            String key = dto.getWorkItem() + "-" + dto.getWorkTypeCode();
            if (uniqueCheckMap.containsKey(key)) {
                throw new BusinessRuntimeException(BizCode.EXCEL_ERROR, String.format("第%d行和第%d行数据重复",
                        uniqueCheckMap.get(key) + 1, i + 1));
            }
            uniqueCheckMap.put(key, i);
        }
    }

    /**
     * 数据库校验：检查数据库中是否已有相同的作业项目和作业类型
     */
    private void validateDatabaseData(List<MWorkItemImportDto> rows) {
        List<MWorkItem> existingItems = findExistingItems(rows);
        if (!existingItems.isEmpty()) {
            StringBuilder existingError = new StringBuilder();
            for (MWorkItem existingItem : existingItems) {
                for (int i = 0; i < rows.size(); i++) {
                    MWorkItemImportDto dto = rows.get(i);
                    if (existingItem.getWorkItem().equals(dto.getWorkItem()) && existingItem.getWorkTypeCode().equals(dto.getWorkTypeCode())) {
                        existingError.append(String.format("第%d行数据数据库中已存在：作业项目【%s】，作业类型【%s】; ", i + 1, existingItem.getWorkItem(), existingItem.getWorkTypeName()));
                        throw new BusinessRuntimeException(BizCode.EXCEL_ERROR, existingError.toString());
                    }
                }
            }
        }
    }

    /**
     * 保存作业项目数据
     */
    private void saveWorkItems(List<MWorkItemImportDto> rows) {
        List<MWorkItem> workItems = MapperingUtils.copyList(rows, MWorkItem.class);
        workItems.forEach((item) ->{
            item.setCreateBy(DefaultCreateByConstant.userId);
            item.setUpdateBy(DefaultCreateByConstant.userId);
            item.setCreateByName(DefaultCreateByConstant.userId.toString());
            item.setUpdateByName(DefaultCreateByConstant.userId.toString());
            item.setCreateTime(LocalDateTime.now());
            item.setUpdateTime(LocalDateTime.now());
        });
        itemService.saveBatch(workItems);
        log.info("成功插入excel中【" + rows.size() + "】条作业项目数据");
    }

    /**
     * 查找已存在的作业项目数据
     */
    private List<MWorkItem> findExistingItems(List<MWorkItemImportDto> rows) {
        List<String> workItems = rows.stream().map(MWorkItemImportDto::getWorkItem).collect(Collectors.toList());
        List<String> workTypeCodes = rows.stream().map(MWorkItemImportDto::getWorkTypeCode).collect(Collectors.toList());

        return itemService.list(new LambdaQueryWrapper<MWorkItem>()
                .in(MWorkItem::getWorkItem, workItems)
                .in(MWorkItem::getWorkTypeCode, workTypeCodes));
    }
}
